﻿namespace Jsl.StyleCop.CSharp.RefactoringRules
{
    using Jsl.StyleCop.CSharp.UnitTesting;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [TestClass]
    public class TestLocalVariableSettingUnused : TestBase<RuleAnalyzer>
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="TestLocalVariableSettingUnused"/> class.
        /// </summary>
        public TestLocalVariableSettingUnused()
            : base(RuleAnalyzer.LocalVariableAssignmentUnused)
        {
        }
        #endregion Constructors

        [TestMethod]
        public void No_variables_is_OK()
        {
            this.TestMethod(
                @"public int Method()
                {
                    return 5;
                }")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Setting_fields_is_ignored()
        {
            this.TestMethod(
                @"public int Method()
                {
                    field = 5;
                    return 6;
                }")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Parameters_are_processed_too()
        {
            this.TestMethod(
                @"public int Method(int parameter)
                {
                    parameter = 5;
                }")
                .AssertViolation(3, "The variable 'parameter' assignment isn't used or is overwritten before it is used.")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Used_variable_setting_is_OK()
        {
            this.TestMethod(
                @"public int Method()
                {
                    int variable = 5;
                    return variable;
                }")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Unused_setting_is_a_problem()
        {
            this.TestMethod(
                @"public int Method()
                {
                    int variable = 5;
                    return 7;
                }")
                .AssertViolation(3, "The variable 'variable' assignment isn't used or is overwritten before it is used.")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Variable_value_is_overwritten_before_use()
        {
            this.TestMethod(
                @"public int Method()
                {
                    int variable = 5;
                    variable = 6;
                    return variable;
                }")
                .AssertViolation(3, "The variable 'variable' assignment isn't used or is overwritten before it is used.")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Variable_value_is_overwritten_in_IF_is_OK()
        {
            this.TestMethod(
                @"public int Method()
                {
                    int variable = 5;
                    if (true)
                    {
                        variable = 6;
                    }

                    return variable;
                }")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Variable_value_is_overwritten_in_out_parameter()
        {
            this.TestMethod(
                @"public int Method()
                {
                    int variable = 5;
                    var dictionary = new Dictionary<string, int>();
                    if (dictionary.TryGetValue(""XXX"", out variable))
                    {
                    }

                    return variable;
                }")
                .AssertViolation(3, "The variable 'variable' assignment isn't used or is overwritten before it is used.")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Variable_only_used_as_an_out_parameter_is_OK()
        {
            this.TestMethod(
                @"public int Method()
                {
                    int variable;
                    if (dictionary.TryGetValue(""XXX"", out variable))
                    {
                    }
                }")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Out_parameters_are_counted_as_used()
        {
            this.TestMethod(
                @"public void Method(out bool parameter)
                {
                    parameter = true;
                }")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Ref_parameters_are_counted_as_used()
        {
            this.TestMethod(
                @"public void Method(ref bool parameter)
                {
                    parameter = true;
                }")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Variable_only_used_as_an_ref_parameter_is_OK()
        {
            this.TestMethod(
                @"public int Method()
                {
                    int variable;
                    if (dictionary.TryGetValue(""XXX"", ref variable))
                    {
                    }
                }")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Catch_variables_are_OK()
        {
            this.TestMethod(
                @"public Exception Method()
                {
                    try
                    {
                    }
                    catch (Exception ex)
                    {
                        return ex;
                    }
                }")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Catch_variables_are_checked()
        {
            this.TestMethod(
                @"public Exception Method()
                {
                    try
                    {
                    }
                    catch (Exception ex)
                    {
                        ex = null;
                        throw ex;
                    }
                }")
                .AssertViolation(6, "The variable 'ex' assignment isn't used or is overwritten before it is used.")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Increment_counts_as_a_usage_followed_by_an_assignment()
        {
            this.TestMethod(
                @"public int Method()
                {
                    int variable = 12;
                    variable += 1;
                    return variable;
                }")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Variables_in_lambdas_are_processed()
        {
            this.TestMethod(
                @"public void Method()
                {
                    Method(() => { int variable = 1; });
                }")
                .AssertViolation(3, "The variable 'variable' assignment isn't used or is overwritten before it is used.")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Variables_in_anonymous_methods_are_processed()
        {
            this.TestMethod(
                @"public void Method()
                {
                    Method(delegate()
                        {
                            int variable = 1;
                        });
                }")
                .AssertViolation(5, "The variable 'variable' assignment isn't used or is overwritten before it is used.")
                .NoMoreViolations();
        }
    }
}